// See LICENSE.Sifive for license details.
#include <stdint.h>

#include <platform.h>

#include "common.h"

#define DEBUG
#include "kprintf.h"

// Total payload in B
#define PAYLOAD_SIZE_B (8 << 10) // default: 3MiB
// A sector is 512 bytes, so (1 << 11) * 512B = 1 MiB
#define SECTOR_SIZE_B 256
// Payload size in # of sectors
#define PAYLOAD_SIZE (PAYLOAD_SIZE_B / SECTOR_SIZE_B)

// The sector at which the BBL partition starts
#define BBL_PARTITION_START_SECTOR 34

#define TL_CLK 100000000
#ifndef TL_CLK
#error Must define TL_CLK
#endif

#define F_CLK TL_CLK

static volatile uint32_t * const spi = (void *)(SPI_CTRL_ADDR);

static inline uint8_t spi_xfer(uint8_t d)
{
	int32_t r;

	REG32(spi, SPI_REG_TXFIFO) = d;
	do {
		r = REG32(spi, SPI_REG_RXFIFO);
	} while (r < 0);
	return r;
}

static inline uint8_t sd_dummy(void)
{
	return spi_xfer(0xFF);
}

static uint8_t sd_cmd(uint8_t cmd, uint32_t arg, uint8_t crc)
{
	unsigned long n;
	uint8_t r;

	REG32(spi, SPI_REG_CSMODE) = SPI_CSMODE_HOLD;
	//sd_dummy();
	spi_xfer(cmd);
	spi_xfer(arg >> 24);
	spi_xfer(arg >> 16);
	spi_xfer(arg >> 8);
	//spi_xfer(arg);
	//spi_xfer(crc);

	//n = 1000;
	//do {
		//r = sd_dummy();
		//if (!(r & 0x80)) {
//			dprintf("sd:cmd: %hx\r\n", r);
			//goto done;
	//	}
	//} while (--n > 0);
	//kputs("sd_cmd: timeout");
//done:
	return 0;
}

static inline void sd_cmd_end(void)
{
	//sd_dummy();
	REG32(spi, SPI_REG_CSMODE) = SPI_CSMODE_AUTO;
}


static void sd_poweron(void)
{
	long i;
	REG32(spi, SPI_REG_SCKDIV) = (F_CLK / 300000UL);
	REG32(spi, SPI_REG_CSMODE) = SPI_CSMODE_OFF;
	for (i = 10; i > 0; i--) {
		sd_dummy();
	}
	REG32(spi, SPI_REG_CSMODE) = SPI_CSMODE_AUTO;
}

static int sd_cmd0(void)
{
	int rc;
	dputs("CMD0S");
	rc = (sd_cmd(0x40, 0, 0x95) != 0x01);
	dputs("CMD0M");
	sd_cmd_end();
	dputs("CMD0E");
	return rc;
}

static int sd_cmd8(void)
{
	int rc;
	dputs("CMD8");
	rc = (sd_cmd(0x48, 0x000001AA, 0x87) != 0x01);
	sd_dummy(); /* command version; reserved */
	sd_dummy(); /* reserved */
	rc |= ((sd_dummy() & 0xF) != 0x1); /* voltage */
	rc |= (sd_dummy() != 0xAA); /* check pattern */
	sd_cmd_end();
	return rc;
}

static void sd_cmd55(void)
{
	sd_cmd(0x77, 0, 0x65);
	sd_cmd_end();
}

static int sd_acmd41(void)
{
	uint8_t r;
	dputs("ACMD41");
	do {
		sd_cmd55();
		r = sd_cmd(0x69, 0x40000000, 0x77); /* HCS = 1 */
	} while (r == 0x01);
	return (r != 0x00);
}

static int sd_cmd58(void)
{
	int rc;
	dputs("CMD58");
	rc = (sd_cmd(0x7A, 0, 0xFD) != 0x00);
	rc |= ((sd_dummy() & 0x80) != 0x80); /* Power up status */
	sd_dummy();
	sd_dummy();
	sd_dummy();
	sd_cmd_end();
	return rc;
}

static int sd_cmd16(void)
{
	int rc;
	dputs("CMD16");
	rc = (sd_cmd(0x50, 0x200, 0x15) != 0x00);
	sd_cmd_end();
	return rc;
}

static uint16_t crc16_round(uint16_t crc, uint8_t data) {
	crc = (uint8_t)(crc >> 8) | (crc << 8);
	crc ^= data;
	crc ^= (uint8_t)(crc >> 4) & 0xf;
	crc ^= crc << 12;
	crc ^= (crc & 0xff) << 5;
	return crc;
}

#define SPIN_SHIFT	6
#define SPIN_UPDATE(i)	(!((i) & ((1 << SPIN_SHIFT)-1)))
#define SPIN_INDEX(i)	(((i) >> SPIN_SHIFT) & 0x3)

static const char spinner[] = { '-', '/', '|', '\\' };

static int copy(void)
{
	volatile uint8_t *p = (void *)(PAYLOAD_DEST);
	long i = PAYLOAD_SIZE;
	int rc = 0;

	dputs("CMD18");

	kprintf("LOADING 0x%xB PAYLOAD\r\n", PAYLOAD_SIZE_B);
	kprintf("LOADING  ");

	// TODO: Speed up SPI freq. (breaks between these two values)
	//REG32(spi, SPI_REG_SCKDIV) = (F_CLK / 16666666UL);
	REG32(spi, SPI_REG_SCKDIV) = (F_CLK / 5000000UL);
	if (sd_cmd(0x52, BBL_PARTITION_START_SECTOR, 0xE1) != 0x00) {
		sd_cmd_end();
		return 1;
	}
	do {
		uint16_t crc, crc_exp;
		long n;

		crc = 0;
		n = SECTOR_SIZE_B;
		while (sd_dummy() != 0xFE);
		do {
			uint8_t x = sd_dummy();
			*p++ = x;
			crc = crc16_round(crc, x);
		} while (--n > 0);

		crc_exp = ((uint16_t)sd_dummy() << 8);
		crc_exp |= sd_dummy();

		if (crc != crc_exp) {
			kputs("\b- CRC mismatch ");
			rc = 1;
			break;
		}

		if (SPIN_UPDATE(i)) {
			kputc('\b');
			kputc(spinner[SPIN_INDEX(i)]);
		}
	} while (--i > 0);
	sd_cmd_end();

	sd_cmd(0x4C, 0, 0x01);
	sd_cmd_end();
	kputs("\b ");
	return rc;
}

static int sd_read_flash(uint32_t start_addr, uint32_t end_addr, uint8_t *buf_ptr) {
    int rc = 0;
    uint8_t response;
    uint32_t addr;

    dputs("S");
    // 发送读取命令，例如CMD_READ (0x03)
    response = sd_cmd(0x03, start_addr, 0x95); // 假设使用CMD_READ命令和3字节地址
    if (response != 0x00) {
        dputs("READERROR");
        rc = 1; // 如果命令执行不成功，设置错误码
        goto read_end;
    }

    dputs("M");

    // 读取数据，从start_addr到end_addr
    for (addr = start_addr; addr < end_addr; addr += SECTOR_SIZE_B) {
        uint8_t sector_data[SECTOR_SIZE_B];

        // 发送地址
       // response = sd_cmd(0x013, addr, 0x95); // 再次发送读取命令和地址
       // if (response != 0x00) {
         //   rc = 1; // 如果命令执行不成功，设置错误码
           // break; // 跳出循环
       // }

        // 读取一个扇区的数据
        for (int i = 0; i < SECTOR_SIZE_B; ++i) {
            sector_data[i] = sd_dummy(); // 读取数据
        }

        // 使用指针操作将读取的数据复制到buffer中
        for (int j = 0; j < SECTOR_SIZE_B; ++j) {
            *(buf_ptr + j) = sector_data[j];
        }
        buf_ptr += SECTOR_SIZE_B;  // 移动指针到下一个扇区的位置
    }

    dputs("E");
    sd_cmd_end(); // 结束命令
    return rc;

read_end:
    sd_cmd_end();
    return rc;
}

int main(void)
{
	uint32_t start_addr = 0; // 起始地址
    uint32_t end_addr = PAYLOAD_SIZE_B; // 结束地址，示例为default: 30MiB
	volatile uint8_t *p = (void *)(PAYLOAD_DEST);
	REG32(uart, UART_REG_TXCTRL) = UART_TXEN;

	kputs("I");
	//sd_poweron();
	//if (sd_cmd0() ||
	//   sd_cmd8() ||
	//    sd_acmd41() ||
	//    sd_cmd58() ||
	//    sd_cmd16() ||
	//    copy()) {
	//	kputs("ERROR");
	//	return 1;
	//}

	//kputs("BOOT");
	//while(1);
	
	if (sd_read_flash(start_addr, end_addr, p)) {
        kputs("Error reading flash");
    } else {
        kputs("O");
    }

	__asm__ __volatile__ ("fence.i" : : : "memory");

	return 0;
}
